#include <vector>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
#include "ConfigFileReader.h"
#include "../config/Config.h"
#include "HtmlReader.h"
#include "Defaults.h"

using namespace kc_sf;

// ------------------------------------------------------------------ //

namespace kc_sf
{
	const std::string CONFIG_FILENAME = "config.xml";
}

// ------------------------------------------------------------------ //

ConfigFileReader & ConfigFileReader::getInstance()
{
	static ConfigFileReader instance(); // stworzenie instancji klasy - Meyers singleton -> sam sie umie zamknąć
	return instance;
}

// ------------------------------------------------------------------ //

ConfigFileReader::ConfigFileReader()
{
	try
	{
		c_tree = HtmlReader( CONFIG_FILENAME  ).read(); // tworzy drzewko tagów
	}
	catch( FileReader::Error &e  )
	{
		std::cerr << "Uwaga! Plik konfiguracyjny: " << CONFIG_FILENAME;
		if( e.type == FileReader::Error::NO_FILE  )
			std::cerr << " - nie istnieje" << std::endl;
		else if( e.type == FileReader::Error::NO_ACCESS )
			std::cerr << " - brak dostępu" << std::endl;
		std::cerr << "Ustawiam domyślne opcje konfiguracyjne" << std::endl;
	}

	// wczytaj wiadomość do pamięci
	//	char *buffer = new char[length];
	 filestr.read(buffer, length );
	 filestr.close();

	 c_input = buffer;
	 delete buffer;
	 ///

	if( !checkTreeNode( c_tree.root() ) )
		std::cerr << "Odczyt pliku konfiguracyjnego nie powiódł się!" << std::endl;
}

// ------------------------------------------------------------------ //

// przeszukiwanie wgłąb
bool ConfigFileReader::checkTreeNode( Node<HtmlTag> &node )
{
	bool success = false;
	const std::string DEF_OPTS = "Wymienione opcje ustawiam na domyślne.";
	const std::string DEF_OPT = "Ustawiam domyślną wartość.";

	// ja
	HtmlTag tag = static_cast<HtmlTag>( node );
	ConfigFileTagType type = typeOf( tag );
	success = checkTag( tag, type );

	if( !success )
	{
		std::cerr << "Niepoprawny znacznik XML: <" << tag.name()
				<< "> - zostanie pominiety" << std::endl;
	}

	// dzieci
	for( unsigned int i = 0; i < node.childrenNr(); ++i )
	{
		success = checkTreeNode( node.child(i ) );
	}

	// ostatni stan się skonczył
	switch( type )
	{
		case MaxSessionsTag:
		case MaxThreadsTag:
		case ListenPortTag:
		case LogsFileTag:
		case LogsLevelTag:
		case SizeTag:
		case AttachmentsNrTag:
		case OccurTag:
		case NameTag:
		case RangeTag:
		case TypeTag:
		case SubtypeTag:
		case LocalpartTag:
		case DomainTag:
		case ActionTag:
			break;

		case KeywordTag:
			if( !c_states.empty() && c_states.top() == KeywordStateTag ) c_states.pop();
			break;

		case HeaderTag:
			if( !c_states.empty() && c_states.top() == RulesTag ) c_states.pop();
			break;

		default:
			if( !c_states.empty() ) c_states.pop();
			break;
	};

	return success;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkTag( const HtmlTag &tag, ConfigFileTagType type_of_tag )
{
	bool success = false;

	switch( type_of_tag )
	{
		case RootTag:
			success = true;
			break;
		case MaxSessionsTag:
			success = checkMaxSessionsTag( tag );
			break;
		case MaxThreadsTag:
			success = checkMaxThreadsTag( tag );
			break;
		case ListenPortTag:
			success = checkListenPortTag( tag );
			break;
		case LogsFileTag:
			success = checkLogsFileTag( tag );
			break;
		case LogsLevelTag:
			success = checkLogsLevelTag( tag );
			break;
		case SizeTag:
			success = checkSizeTag( tag );
			break;
		case AttachmentsNrTag:
			success = checkAttachmentsNrTag( tag );
			break;
		case KeywordTag:
			success = checkKeywordTag( tag );
			break;
		case OccurTag:
			success = checkOccurencesTag( tag );
			break;
		case NameTag:
			success = checkNameTag( tag );
			break;
		case RangeTag:
			success = checkRangeTag( tag );
			break;
		case TypeTag:
			success = checkTypeTag( tag );
			break;
		case SubtypeTag:
			success = checkSubtypeTag( tag );
			break;
		case HeaderTag:
			success = checkHeaderTag( tag );
			break;
		case LocalpartTag:
			success = checkLocalpartTag( tag );
			break;
		case DomainTag:
			success = checkDomainTag( tag );
			break;
		case ActionTag:
			success = checkActionTag( tag );
			break;
		case ConfTag:
			success = checkConfigurationTag( tag );
			break;
		case CondTag:
			success = checkConditionsTag( tag );
			break;
		case RulesTag:
			success = checkRulesTag( tag );
			break;
		case RuleTag:
			success = checkRuleTag( tag );
			break;
		case MessageTag:
			success = checkMessageTag( tag );
			break;
		case AttachmentTag:
			success = checkAttachmentTag( tag );
			break;
		case AddressTag:
			success = checkAddressTag( tag );
			break;
		case DateTag:
			success = checkDateTag( tag );
			break;
		case ContentTypeTag:
			success = checkContentTypeTag( tag );
			break;
		case UndefinedConfigFileTag:
		default:
			break;
	};

	return success;
}

// ------------------------------------------------------------------ //

ConfigFileTagType ConfigFileReader::typeOf( const HtmlTag &tag ) const
{
	ConfigFileTagType type = UndefinedConfigFileTag;

	type = boost::algorithm::iequals( tag.name(), "root" ) ? ConfigFileTagType::RootTag
	: boost::algorithm::iequals( tag.name(), "max_sessions" ) ? ConfigFileTagType::MaxSessionsTag
	: boost::algorithm::iequals( tag.name(), "max_threads" ) ? ConfigFileTagType::MaxThreadsTag
	: boost::algorithm::iequals( tag.name(), "listen_port" ) ? ConfigFileTagType::ListenPortTag
	: boost::algorithm::iequals( tag.name(), "logs" ) ? ConfigFileTagType::LogsFileTag
	: boost::algorithm::iequals( tag.name(), "logs_level" ) ? ConfigFileTagType::LogsLevelTag
	: boost::algorithm::iequals( tag.name(), "size" ) ? ConfigFileTagType::SizeTag
	: boost::algorithm::iequals( tag.name(), "attachments_nr" ) ? ConfigFileTagType::AttachmentsNrTag
	: boost::algorithm::iequals( tag.name(), "occurences" ) ? ConfigFileTagType::OccurTag
	: boost::algorithm::iequals( tag.name(), "keyword" ) ? ConfigFileTagType::KeywordTag
	: boost::algorithm::iequals( tag.name(), "name" ) ? ConfigFileTagType::NameTag
	: boost::algorithm::iequals( tag.name(), "range" ) ? ConfigFileTagType::RangeTag
	: boost::algorithm::iequals( tag.name(), "type" ) ? ConfigFileTagType::TypeTag
	: boost::algorithm::iequals( tag.name(), "subtype" ) ? ConfigFileTagType::SubtypeTag
	: boost::algorithm::iequals( tag.name(), "header" ) ? ConfigFileTagType::HeaderTag
	: boost::algorithm::iequals( tag.name(), "localpart" ) ? ConfigFileTagType::LocalpartTag
	: boost::algorithm::iequals( tag.name(), "domain" ) ? ConfigFileTagType::DomainTag
	: boost::algorithm::iequals( tag.name(), "action" ) ? ConfigFileTagType::ActionTag
	: boost::algorithm::iequals( tag.name(), "configuration" ) ? ConfigFileTagType::ConfTag
	: boost::algorithm::iequals( tag.name(), "conditions" ) ? ConfigFileTagType::CondTag
	: boost::algorithm::iequals( tag.name(), "rules" ) ? ConfigFileTagType::RulesTag
	: boost::algorithm::iequals( tag.name(), "rule" ) ? ConfigFileTagType::RuleTag
	: boost::algorithm::iequals( tag.name(), "message" ) ? ConfigFileTagType::MessageTag
	: boost::algorithm::iequals( tag.name(), "attachment" ) ? ConfigFileTagType::AttachmentTag
	: boost::algorithm::iequals( tag.name(), "address" ) ? ConfigFileTagType::AddressTag
	: boost::algorithm::iequals( tag.name(), "date" ) ? ConfigFileTagType::DateTag
	: boost::algorithm::iequals( tag.name(), "content_type" ) ? ConfigFileTagType::ContentTypeTag
	: ConfigFileTagType::UndefinedConfigFileTag;

	return type;
}

/
// ------------------------------------------------------------------ //

int ConfigFileReader::mapHeaderTypeToInt(const std::string &value_str )
{
	return( (boost::algorithm::iequals(value_str, "from" ) == 0 ) ? AddressCondition::FROM
			:( (boost::algorithm::iequals(value_str, "to" ) == 0 ) ? AddressCondition::TO
					: AddressCondition::UNDEFINED ) );
}

// ------------------------------------------------------------------ //

int ConfigFileReader::mapRangeToInt(const std::string &value_str )
{
	return( (boost::algorithm::iequals(value_str, "header" ) == 0 ) ? KeywordCondition::HEADER
			:( (boost::algorithm::iequals(value_str, "body" ) == 0 ) ? KeywordCondition::BODY
					: KeywordCondition::ALL ) );
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkMaxSessionsTag( const HtmlTag &tag )
{
	unsigned int val = convertTagValueToUInt( tag );
	if( val != static_cast<unsigned int>( -1 ) )
	{
		if( !c_states.empty() && c_states.top() == ConfTag )
		{
			c_max_sessions = val;
			return true;
		}
	}

	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkMaxThreadsTag( const HtmlTag &tag )
{
	unsigned int val = convertTagValueToUInt(tag );
	if( val != static_cast<unsigned int>( -1 ) ) {
		if( !c_states.empty() && c_states.top() == ConfTag ) {
			c_max_threads = val;
			return true;
		}
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkListenPortTag( const HtmlTag &tag )
{
	unsigned int val = convertTagValueToUInt(tag );
	if( val != static_cast<unsigned int>( -1 ) ) {
		if( !c_states.empty() && c_states.top() == ConfTag ) {
			//@todo spr dostepne zakresy portow
			c_listen_port = val;
			return true;
		}
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkLogsFileTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == ConfTag ) {
		c_logs_file_path = tag.value();
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkLogsLevelTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == ConfTag ) {
		c_logs_level = Logger::mapLevelNameToInt( tag.value() );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkSizeTag( const HtmlTag &tag )
{
	bool success = false;

	if( c_states.empty() ) return success;

	unsigned int val = convertTagValueToUInt(tag );
	//@todo zawarty w tagu-rodzicu ""
	if( val == static_cast<unsigned int>( -1 ) ) {
		//		std::cerr <<
		return success;
	}

	std::string op;
	if( c_states.top() == MESSAGE_S || c_states.top() == AttachmentTag ) {
		// wydzielenie atrybutu operatora ze znacznika
		op = tag.attribute("op" );
		if( op.empty() ) op = "ge";
	}

	if( c_states.top() == MESSAGE_S ) {
		MsgConditionPtr msg = boost::dynamic_pointer_cast<MsgCondition>(
				c_last_cond );

		if( msg == NULL ) throw ConfigError(
				"ConfigFileReader: verifyUInt. Chcę ustawić size, ale nie istnieje obiekt MsgCondition" );

		msg->setSize(UIntParam(val, op ) );
		success = true;
	} else if( c_states.top() == AttachmentTag ) {
		AttachmentConditionPtr ent = boost::dynamic_pointer_cast<
				AttachmentCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyUInt. Chcę ustawić size, ale nie istnieje obiekt AttachmentCondition" );

		ent->setSize(UIntParam(val, op ) );
		success = true;
	}
	return success;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkAttachmentsNrTag( const HtmlTag &tag )
{
	bool success = false;

	if( c_states.empty() ) return success;
	if( c_states.top() == MESSAGE_S ) {
		unsigned int val = convertTagValueToUInt(tag );
		if( val != static_cast<unsigned int>( -1 ) ) {
			MsgConditionPtr msg = boost::dynamic_pointer_cast<MsgCondition>(
					c_last_cond );

			if( msg == NULL ) throw ConfigError(
					"ConfigFileReader: verifyUInt. Chcę ustawić attachment_nr, ale nie istnieje obiekt AttachmentCondition" );

			// wydzielenie atrybutu operatora ze znacznika
			std::string op = tag.attribute("op" );
			if( op.empty() ) op = "ge";

			msg->setAttachmentNr(UIntParam(val, op ) );
			success = true;
		}
	}
	if( !c_states.empty() && c_states.top() == RulesTag ) {
		c_states.push(AttachmentTag );
		if( c_last_cond != NULL ) c_last_conditions.push_back(c_last_cond );
		c_last_cond = ConditionPtr(new AttachmentCondition() );
		success = true;
	}
	return success;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkKeywordTag( const HtmlTag &tag )
{
	if( c_states.empty() ) return false;

	if( c_states.top() == HeaderStateTag ) {
		HeaderConditionPtr ent = boost::dynamic_pointer_cast<HeaderCondition>(
				c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić keyword, ale nie istnieje obiekt HeaderCondition" );

		// wydzielenie atrybutu ze znacznika
		std::string op = tag.attribute("match" );
		if( op.empty() ) op = "contain";

		ent->setKeyword(StringParam(tag.value(), op ) );
		return true;
	} else if( c_states.top() == RulesTag ) {
		c_states.push(KeywordStateTag );
		if( c_last_cond != NULL ) c_last_conditions.push_back(c_last_cond );
		c_last_cond = ConditionPtr(new KeywordCondition() );
		return true;
	}

	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkOccurencesTag( const HtmlTag &tag )
{
	bool success = false;

	if( c_states.empty() ) return success;

	unsigned int val = convertTagValueToUInt(tag );
	if( val == static_cast<unsigned int>( -1 ) ) {
		//@todo zawarty w tagu ...
		return success;
	}

	switch( c_states.top() ) {
	case AttachmentTag: {
		AttachmentConditionPtr ent = boost::dynamic_pointer_cast<
				AttachmentCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyUInt. Chcę ustawić occurences, ale nie istnieje obiekt AttachmentCondition" );

		// wydzielenie atrybutu operatora ze znacznika
		std::string op = tag.attribute("op" );
		if( op.empty() ) op = "ge";

		ent->setOccurences(UIntParam(val, op ) );
		success = true;
	}
		break;
	case HeaderStateTag: {
		HeaderConditionPtr ent = boost::dynamic_pointer_cast<HeaderCondition>(
				c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyUInt. Chcę ustawić occurences, ale nie istnieje obiekt HeaderCondition" );

		// wydzielenie atrybutu operatora ze znacznika
		std::string op = tag.attribute("op" );
		if( op.empty() ) op = "ge";

		ent->setOccurences(UIntParam(val, op ) );
		success = true;
	}
		break;
	case KeywordStateTag: {
		KeywordConditionPtr ent =
				boost::dynamic_pointer_cast<KeywordCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyUInt. Chcę ustawić occurences, ale nie istnieje obiekt KeywordCondition" );

		// wydzielenie atrybutu operatora ze znacznika
		std::string op = tag.attribute("op" );
		if( op.empty() ) op = "ge";

		ent->setOccurences(UIntParam(val, op ) );
		success = true;
	}
		break;
	default:
		break;
	}

	return success;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkNameTag( const HtmlTag &tag )
{
	bool success = false;
	if( c_states.empty() ) return success;

	// wydzielenie atrybutu ze znacznika
	std::string op = tag.attribute("match" );
	if( op.empty() ) op = "contain";

	switch( c_states.top() ) {
	case HeaderStateTag: {
		HeaderConditionPtr ent = boost::dynamic_pointer_cast<HeaderCondition>(
				c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić name, ale nie istnieje obiekt HeaderCondition" );

		ent->setName(StringParam(tag.value(), op ) );
		success = true;
	}
		break;
	case KeywordStateTag: {
		KeywordConditionPtr ent =
				boost::dynamic_pointer_cast<KeywordCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić name, ale nie istnieje obiekt KeywordCondition" );

		ent->setName(StringParam(tag.value(), op ) );
		success = true;
	}
		break;
	default:
		break;
	};

	return success;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkRangeTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == KeywordStateTag ) // ListParam
	{
		KeywordConditionPtr ent =
				boost::dynamic_pointer_cast<KeywordCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić range, ale nie istnieje obiekt KeywordCondition" );

		(boost::algorithm::iequals(value_str, "header" ) == 0 ) ? KeywordRange::Headers
				:( (boost::algorithm::iequals(value_str, "body" ) == 0 ) ? KeywordRange::Body
			: KeywordRange::All ) );

		ent->setRange(ListParam(tag.value(), &ConfigFileReader::mapRangeToInt ) );
		return true;
	}
	return false;
}

bool ConfigFileReader::checkTypeTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == ContentTypeTag ) {
		AttachmentConditionPtr ent = boost::dynamic_pointer_cast<
				AttachmentCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić type lub subtype, ale nie istnieje obiekt AddressCondition" );

		// wydzielenie atrybutu ze znacznika
		std::string op = tag.attribute("match" );
		if( op.empty() ) op = "contain";

		ent->setContentType(StringParam(tag.value(), op ) );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkSubtypeTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == ContentTypeTag ) {
		AttachmentConditionPtr ent = boost::dynamic_pointer_cast<
				AttachmentCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić type lub subtype, ale nie istnieje obiekt AddressCondition" );

		// wydzielenie atrybutu ze znacznika
		std::string op = tag.attribute("match" );
		if( op.empty() ) op = "contain";

		ent->setContentSubtype(StringParam(tag.value(), op ) );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkHeaderTag( const HtmlTag &tag )
{
	bool success = false;
	if( c_states.empty() ) return success;

	if( c_states.top() == AddressTag ) // ListParam
	{
		AddressConditionPtr ent =
				boost::dynamic_pointer_cast<AddressCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić header, ale nie istnieje obiekt AddressCondition" );

		ent->setHeaderType(ListParam(tag.value(),
				&ConfigFileReader::mapHeaderTypeToInt ) );
		success = true;
	}
	if( c_states.top() == RulesTag ) {
		if( c_last_cond != NULL ) c_last_conditions.push_back(c_last_cond );
		c_last_cond = ConditionPtr(new HeaderCondition() );
		c_states.push(HeaderStateTag );
		success = true;
	}
	return success;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkLocalpartTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == AddressTag ) {
		AddressConditionPtr ent =
				boost::dynamic_pointer_cast<AddressCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić localpart lub domain, ale nie istnieje obiekt AddressCondition" );

		// wydzielenie atrybutu ze znacznika
		std::string op = tag.attribute("match" );
		if( op.empty() ) op = "contain";

		ent->setLocalpart(StringParam(tag.value(), op ) );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkDomainTag( const HtmlTag &tag )
{
	bool success = false;

	if( c_states.empty() ) return success;

	if( c_states.top() == ConfTag ) {
		c_domain
				=( boost::algorithm::istarts_with(tag.value(), "localhost" ) ) ? "127.0.0.1"
						: tag.value();
		success = true;
	} else if( c_states.top() == AddressTag ) {
		AddressConditionPtr ent =
				boost::dynamic_pointer_cast<AddressCondition>(c_last_cond );

		if( ent == NULL ) throw ConfigError(
				"ConfigFileReader: verifyString. Chcę ustawić localpart lub domain, ale nie istnieje obiekt AddressCondition" );

		// wydzielenie atrybutu ze znacznika
		std::string op = tag.attribute("match" );
		if( op.empty() ) op = "contain";

		ent->setDomain(StringParam(tag.value(), op ) );
		success = true;
	}
	return success;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkActionTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == RulesTag ) {
		// wydzielenie atrybutów ze znacznika
		std::string type = tag.attribute("type" ), pr =
				tag.attribute("priority" );
		if( !type.empty() && !pr.empty() ) {
			int priority = 1;

			try {
				priority = boost::lexical_cast<int>(pr );
			} catch( boost::bad_lexical_cast &err ) {
				return false;
			}

			Action::Type atype = Action::mapActionNameToType(type );
			Action action(atype, priority );

			// uzupełnij listę reguł
			if( c_last_cond != NULL ) c_last_conditions.push_back(c_last_cond );
			c_rules.push_back(Rule(c_last_conditions, action ) );
			c_last_conditions.clear();
			c_last_cond = ConditionPtr();

			return true;
		}
	}

	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkConfigurationTag( const HtmlTag &tag )
{
	if( c_states.empty() ) {
		c_states.push(ConfTag );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkRulesTag( const HtmlTag &tag )
{
	if( c_states.empty() ) {
		c_states.push(RulesTag );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkRuleTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == RulesTag ) {
		c_states.push(RulesTag );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkConditionsTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == RulesTag ) {
		// informacja o logice
		std::string logic = tag.attribute("logic" );
		if( logic.empty() ) logic = "and";
		ConditionsList::Logic l = ConditionsList::AND;
		if( boost::algorithm::iequals(logic, "or" ) == 0 ) l = ConditionsList::OR;
		c_last_conditions.setLogic(l );
		c_states.push(RulesTag );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkMessageTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == RulesTag ) {
		c_states.push(MESSAGE_S );
		if( c_last_cond != NULL ) c_last_conditions.push_back(c_last_cond );
		c_last_cond = ConditionPtr(new MsgCondition() );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

//@todo v2
bool ConfigFileReader::checkAttachmentTag( const HtmlTag &tag )
{
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkAddressTag( const HtmlTag &tag )
{

	if( !c_states.empty() && c_states.top() == RulesTag ) {
		c_states.push(AddressTag );
		if( c_last_cond != NULL ) c_last_conditions.push_back(c_last_cond );
		c_last_cond = ConditionPtr(new AddressCondition() );
		return true;
	}
	return false;
}

// ------------------------------------------------------------------ //

//@todo v2
bool ConfigFileReader::checkDateTag( const HtmlTag &tag )
{
	return false;
}

// ------------------------------------------------------------------ //

bool ConfigFileReader::checkContentTypeTag( const HtmlTag &tag )
{
	if( !c_states.empty() && c_states.top() == AttachmentTag ) {
		c_states.push(ContentTypeTag );
		return true;
	}
	return false;
}

Logic::Symbol * ConfigFileReader::createAnrCondition( const HtmlTag &tag ) const
{
	Operator op = convertTagValue <Operator> ( tag );
	int anr = convertTagValue <int> ( tag );
	return new MsgANrCond( op, anr );
}

// --------------------------------------------------- //

Logic::Symbol * ConfigFileReader::createAttachmentCondition( const HtmlTag &tag ) const
{
	enum
	{
		ConditionId = 0,
		SizeOperator,
		Size,
		ContentType,
		ContentSubtype,
		NameOperator,
		Name,
		ExtensionOperator,
		Extension,
		HowManyOperator,
		HowMany,
	};

	std::string
		size_op_str = values[ SizeOperator ],
		name_op_str = values[ NameOperator ],
		extension_op_str = values[ ExtensionOperator ],
		content_type_str = values[ ContentType ];

	AttchCond *attch_cond = new AttchCond();
	if( !boost::iequals( "NULL", size_op_str ) )
	{
		Operator op = boost::lexical_cast<Operator>( size_op_str );
		unsigned long size = boost::lexical_cast<unsigned long>( values[ Size ] );
		attch_cond->aSizeCondition = AttchSizeCond( op, size );
	}

	if( !boost::iequals( "NULL", name_op_str ) )
	{
		Operator op = boost::lexical_cast<Operator>( name_op_str );
		attch_cond->aFilenameCondition = AttchFilenameCond( op, values[ Name ] );
	}

	if( !boost::iequals( "NULL", extension_op_str ) )
	{
		Operator op = boost::lexical_cast<Operator>( extension_op_str );
		attch_cond->aExtensionCondition = AttchFilenameCond( op, values[ Extension ] );
	}

	if( !boost::iequals( "NULL", content_type_str ) && !content_type_str.empty() )
		attch_cond->aContentTypeCondition = AttchCTypeCond( content_type_str, values[ ContentSubtype ] );

	attch_cond->hm_how_many = boost::lexical_cast<int>( values[ HowMany ] );
	attch_cond->hm_operator = boost::lexical_cast<Operator>( values[ HowManyOperator ] );

	return attch_cond;
}

// --------------------------------------------------- //

Logic::Symbol * ConfigFileReader::createDateCondition( const HtmlTag &tag ) const
{
	enum { ConditionId = 0, From, To };

	boost::posix_time::ptime
		from( boost::posix_time::time_from_string( values[ From ] ) ),
		to( boost::posix_time::time_from_string( values[ To ] ) );

	return new DateCond( from, to );
}

// --------------------------------------------------- //

Logic::Symbol * ConfigFileReader::createFromCondition( const HtmlTag &tag ) const
{
	enum Column
	{
		ConditionId = 0,
		Localpart,
		Domain,
		AddressType
	};

	return new FromCond( values[ Localpart ], values[ Domain ] );
}

// --------------------------------------------------- //

Logic::Symbol * ConfigFileReader::createKeywordCondition( const HtmlTag &tag ) const
{
	enum
	{
		ConditionId = 0,
		KeywordOperator,
		Keyword,
		KeywordRange,
		HowManyOperator,
		HowMany,
	};

	Operator keyword_op = boost::lexical_cast <std::string>( values[ KeywordOperator ] );
	KeywordRange range = boost::lexical_cast <KeywordRange>( values[ KeywordRange ] );

	KeywordCond *keyword_cond = new KeywordCond( op, values[ Keyword ], range );
	keyword_cond->hm_how_many = boost::lexical_cast<int>( values[ HowMany ] );
	keyword_cond->hm_operator = boost::lexical_cast<Operator>( values[ HowManyOperator ] );

	return keyword_cond;
}

// --------------------------------------------------- //

Logic::Symbol * ConfigFileReader::createResentCondition( const HtmlTag &tag ) const
{
	enum { ConditionId = 0, Resent };

	return new MsgResentCond( boost::lexical_cast <bool>( values[ Resent ] ) );
}

// --------------------------------------------------- //

Logic::Symbol * ConfigFileReader::createSizeCondition( const HtmlTag &tag ) const
{
	enum { ConditionId = 0, SizeOperator, Size };

	int size = boost::lexical_cast<int>( values[ Size ] );
	Operator op = boost::lexical_cast<Operator>( values[ SizeOperator ] );

	return new MsgSizeCond( size, op );
}

// --------------------------------------------------- //

Logic::Symbol * ConfigFileReader::createToCondition( const HtmlTag &tag ) const
{
	enum Column
	{
		ConditionId = 0,
		Localpart,
		Domain,
		AddressType
	};

	return new ToCond( values[ Localpart ], values[ Domain ] );
}
